Skip to content

Escrow contract switch#554

Merged
bogdanfazakas merged 6 commits into
mainfrom
feat/contract-dropdown
Jul 7, 2026
Merged

Escrow contract switch#554
bogdanfazakas merged 6 commits into
mainfrom
feat/contract-dropdown

Conversation

@bogdanfazakas

@bogdanfazakas bogdanfazakas commented Jul 2, 2026

Copy link
Copy Markdown
Member

Fixes #552 .

Changes proposed in this PR:

  • Adds an optional NEXT_PUBLIC_LEGACY_ESCROW_ADDRESS env variable. When set and only on Base the escrow page (/profile/escrow) shows an Escrow contract dropdown with Current contract
  • Selecting Legacy switches the whole page to the old deployment: available/locked balances, active locks, and authorizations are read from the legacy contract, and Withdraw sends the transaction to it for both EOA (MetaMask/ethers signer) and smart-account (Alchemy/viem) wallets.
  • Legacy mode is view + withdraw only: the deposit form is replaced with a hint, and authorization Create/Edit/Revoke are hidden (cards render read-only). A "Withdraw only" chip and a hint under the selector make the mode obvious.
  • When the env var is unset or invalid, or on Sepolia, the selector is hidden and the page behaves exactly as before. The run-job payment flow and the node-details withdraw modal are untouched and always use the current contract.

Summary by CodeRabbit

  • New Features
    • Added a legacy escrow contract selector (current vs legacy) on the escrow page.
    • Added a dismissible legacy escrow warning banner with an optional “Manage escrow” link.
  • Bug Fixes
    • Escrow balances, authorizations, locks, and withdrawals now follow the selected escrow contract version.
    • Legacy mode now enforces withdraw-only behavior by hiding authorization/deposit actions.
  • Documentation
    • Documented the new optional NEXT_PUBLIC_LEGACY_ESCROW_ADDRESS environment variable.
  • Style
    • Added legacy-specific banner, hint, and contract selector layout styling.

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nodes-dashboard Ready Ready Preview, Comment Jul 6, 2026 4:08pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c2b9e608-f8f7-4737-8037-2d940f1c3950

📥 Commits

Reviewing files that changed from the base of the PR and between 1d4f3ad and 9524f71.

📒 Files selected for processing (6)
  • src/components/escrow/escrow-page.module.css
  • src/components/escrow/escrow-page.tsx
  • src/components/escrow/escrow-token-panel.module.css
  • src/components/escrow/escrow-token-panel.tsx
  • src/components/homepage/legacy-escrow-banner.tsx
  • src/lib/use-escrow-data.ts
✅ Files skipped from review due to trivial changes (1)
  • src/components/escrow/escrow-page.module.css
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/components/escrow/escrow-page.tsx
  • src/components/homepage/legacy-escrow-banner.tsx
  • src/lib/use-escrow-data.ts
  • src/components/escrow/escrow-token-panel.tsx

📝 Walkthrough

Walkthrough

Adds legacy escrow contract selection on the escrow page, routes reads and withdrawals through a selectable escrow address, disables deposit and authorization actions in legacy mode, and shows a dismissible homepage banner when the legacy address is configured.

Changes

Legacy Escrow Contract Selector

Layer / File(s) Summary
Legacy escrow constant and env docs
README.md, src/constants/escrow.ts
Documents NEXT_PUBLIC_LEGACY_ESCROW_ADDRESS and exports EscrowContractVersion plus LEGACY_ESCROW_ADDRESS from validated Base-chain config.
OceanProvider escrow address override
src/lib/ocean-provider.ts
Allows escrow contract reads and EOA withdrawals to target an optional escrow address override.
useEscrowData contract-aware loading
src/lib/use-escrow-data.ts
Reloads escrow data for the selected contract, guards stale async updates, and filters locks for the active payer/token.
useWithdrawTokens escrow target resolution
src/lib/use-withdraw-tokens.ts
Adds an optional escrow address to withdraw params and resolves both withdrawal paths to the selected escrow target.
Escrow page contract selector UI
src/components/escrow/escrow-page.tsx, src/components/escrow/escrow-page.module.css
Adds contract switching state, legacy indicator UI, and passes the selected escrow address into token panels.
EscrowTokenPanel legacy-mode restrictions
src/components/escrow/escrow-token-panel.tsx, src/components/escrow/escrow-token-panel.module.css
Makes deposits and authorization management unavailable in legacy mode and updates withdrawal and empty-state behavior.
Homepage legacy escrow banner
src/components/homepage/hero-section.tsx, src/components/homepage/legacy-escrow-banner.tsx, src/components/homepage/legacy-escrow-banner.module.css
Adds a dismissible banner in the homepage hero that appears when the legacy escrow address is configured.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: alexcos20, mihaisc

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: adding support for switching escrow contracts.
Linked Issues check ✅ Passed The changes implement the legacy escrow selector, legacy data reads, withdraw support, and restricted legacy-mode actions required by #552.
Out of Scope Changes check ✅ Passed The edits stay focused on escrow contract switching and related UI/docs/styles, with no clearly unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/contract-dropdown

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@giurgiur99

Copy link
Copy Markdown
Contributor

/run-security-scan

@alexcos20 alexcos20 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI automated code review (Gemini 3).

Overall risk: low

Summary:
This PR is exceptionally well-implemented. It handles the legacy escrow contract redeployment seamlessly by adding a fallback selector and safe read-only/withdraw modes. The approach used to handle React race conditions during data fetching is highly commendable. All edge cases regarding disabled deposits and authorizations on the legacy contract have been thoroughly accounted for.

Comments:
• [INFO][bug] It is a good practice to wrap localStorage access in a try...catch block. In some environments (like Safari Private Browsing or restricted iframes), accessing localStorage throws a SecurityError or QuotaExceededError, which could cause the React component to crash during mount or user interaction.

Consider wrapping the logic safely:

-    setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    try {
+      setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    } catch (err) {
+      setVisible(true);
+    }

And in your dismiss handler:

-    localStorage.setItem(DISMISSED_KEY, 'true');
-    setVisible(false);
+    try {
+      localStorage.setItem(DISMISSED_KEY, 'true');
+    } catch (err) {
+      // ignore
+    }
+    setVisible(false);

• [INFO][style] The way you handled race conditions with loadIdRef and escrowAddressRef is excellent! Ensuring that late-arriving data from an old contract fetch doesn't overwrite the current state is a very robust pattern for data fetching in React.
• [INFO][other] Great job on preventing deposits to the legacy contract right at the UI level while keeping the hook usage clean.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/escrow/escrow-token-panel.tsx (1)

220-254: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Withdraw/deposit form values persist across contract switch.

EscrowTokenPanel isn't remounted when escrowAddress changes (no key change), and there's no effect resetting depositForm/withdrawForm on that change. A previously-entered amount from the current contract's view carries over when switching to the legacy view (or back), which can confuse users even though submission still correctly targets the resolved escrowAddress.

💡 Proposed fix: reset forms when escrowAddress changes
 const EscrowTokenPanel = ({ token, spenders, loadingSpenders, onChange, escrowAddress }: EscrowTokenPanelProps) => {
   const [isCreateOpen, setIsCreateOpen] = useState(false);
   const isLegacy = !!escrowAddress;
+
+  useEffect(() => {
+    depositForm.resetForm();
+    withdrawForm.resetForm();
+  }, [escrowAddress]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/escrow/escrow-token-panel.tsx` around lines 220 - 254, The
deposit and withdraw amounts in EscrowTokenPanel are persisting when
escrowAddress changes because the component is reused and the Formik state is
never reset. Add an effect in EscrowTokenPanel that watches escrowAddress and
calls both depositForm.resetForm() and withdrawForm.resetForm() whenever the
contract view switches, so the amount fields clear when moving between legacy
and current escrow views.
🧹 Nitpick comments (2)
src/components/homepage/legacy-escrow-banner.module.css (1)

8-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Magic-number coupling to hero-section.module.css breakpoints.

The negative margin-top values and breakpoints are hand-copied to mirror another file's layout, per the comment. Any future change to hero padding/breakpoints silently desyncs this file since there's no shared constant/variable enforcing the relationship.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/homepage/legacy-escrow-banner.module.css` around lines 8 - 24,
The banner spacing in the legacy escrow CSS is hard-coupled to hero-section
layout values via copied breakpoints and negative margins. Refactor the .banner
rules to use shared design tokens or CSS custom properties sourced from the hero
layout, and update the `@media` thresholds to reference the same centralized
values so both files stay in sync. Use the existing .banner selector and the
hero-section.module.css breakpoint/padding definitions as the single source of
truth.
src/components/homepage/legacy-escrow-banner.tsx (1)

20-31: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Guard localStorage access against exceptions.

localStorage.getItem/setItem are called unguarded. In restrictive environments (e.g., private browsing modes, storage disabled by policy), these calls can throw and crash the component since there's no error boundary/try-catch here.

🛡️ Proposed defensive fix
   useEffect(() => {
-    setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    try {
+      setVisible(localStorage.getItem(DISMISSED_KEY) !== 'true');
+    } catch {
+      setVisible(true);
+    }
   }, []);

   const dismiss = () => {
-    localStorage.setItem(DISMISSED_KEY, 'true');
-    setVisible(false);
+    try {
+      localStorage.setItem(DISMISSED_KEY, 'true');
+    } catch {
+      // ignore storage errors; still hide for this session
+    }
+    setVisible(false);
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/homepage/legacy-escrow-banner.tsx` around lines 20 - 31, The
localStorage calls in LegacyEscrowBanner are unguarded and can throw in
restricted environments. Update the useEffect initializer and the dismiss
handler to wrap localStorage.getItem and localStorage.setItem in defensive
try/catch logic, and fall back to a safe default if storage is unavailable. Keep
the fix localized to LegacyEscrowBanner and its dismiss function so the
component still renders without crashing when storage access fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/escrow/escrow-token-panel.tsx`:
- Around line 220-254: The deposit and withdraw amounts in EscrowTokenPanel are
persisting when escrowAddress changes because the component is reused and the
Formik state is never reset. Add an effect in EscrowTokenPanel that watches
escrowAddress and calls both depositForm.resetForm() and
withdrawForm.resetForm() whenever the contract view switches, so the amount
fields clear when moving between legacy and current escrow views.

---

Nitpick comments:
In `@src/components/homepage/legacy-escrow-banner.module.css`:
- Around line 8-24: The banner spacing in the legacy escrow CSS is hard-coupled
to hero-section layout values via copied breakpoints and negative margins.
Refactor the .banner rules to use shared design tokens or CSS custom properties
sourced from the hero layout, and update the `@media` thresholds to reference the
same centralized values so both files stay in sync. Use the existing .banner
selector and the hero-section.module.css breakpoint/padding definitions as the
single source of truth.

In `@src/components/homepage/legacy-escrow-banner.tsx`:
- Around line 20-31: The localStorage calls in LegacyEscrowBanner are unguarded
and can throw in restricted environments. Update the useEffect initializer and
the dismiss handler to wrap localStorage.getItem and localStorage.setItem in
defensive try/catch logic, and fall back to a safe default if storage is
unavailable. Keep the fix localized to LegacyEscrowBanner and its dismiss
function so the component still renders without crashing when storage access
fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 729b9975-ff6d-4e15-b507-9bbf9e81ae74

📥 Commits

Reviewing files that changed from the base of the PR and between bb82fb7 and d61ab2c.

📒 Files selected for processing (12)
  • README.md
  • src/components/escrow/escrow-page.module.css
  • src/components/escrow/escrow-page.tsx
  • src/components/escrow/escrow-token-panel.module.css
  • src/components/escrow/escrow-token-panel.tsx
  • src/components/homepage/hero-section.tsx
  • src/components/homepage/legacy-escrow-banner.module.css
  • src/components/homepage/legacy-escrow-banner.tsx
  • src/constants/escrow.ts
  • src/lib/ocean-provider.ts
  • src/lib/use-escrow-data.ts
  • src/lib/use-withdraw-tokens.ts

@dnsi0

dnsi0 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The escrow inputs can be improved, as they look disabled and don't indicate that the user should enter a value
image

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/components/homepage/hero-section.module.css (2)

72-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Magic numbers duplicate hero's nav-overlap geometry.

-80px/-90px/-120px mirror .root's --nav-overlap and its own breakpoint overrides but aren't derived from them. If the hero's overlap or padding changes, these values will silently drift out of sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/homepage/hero-section.module.css` around lines 72 - 84, The
.legacyEscrowBanner spacing is hard-coded with magic numbers that duplicate the
hero nav-overlap values and can drift from .root. Update the
hero-section.module.css rules for .legacyEscrowBanner to derive its negative
margin from the same --nav-overlap geometry used by .root, preferably via a
shared CSS variable or calc() expression across the matching media breakpoints,
so the banner stays in sync if the hero overlap changes.

72-86: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the hero overlap offset here
The margin-top values here duplicate the hero’s breakpoint-specific nav overlap. Pull this from the same custom property or shared rule so the banner stays aligned if the hero spacing changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/homepage/hero-section.module.css` around lines 72 - 86, The
.legacyEscrowBanner margin-top values are duplicating the hero’s
breakpoint-specific overlap spacing, so update this rule to reuse the same
shared custom property or overlapping spacing source used by the hero section.
Keep the existing breakpoint behavior in sync by referencing the shared hero
offset in hero-section.module.css rather than hardcoding separate values here.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/homepage/hero-section.module.css`:
- Around line 72-84: The .legacyEscrowBanner spacing is hard-coded with magic
numbers that duplicate the hero nav-overlap values and can drift from .root.
Update the hero-section.module.css rules for .legacyEscrowBanner to derive its
negative margin from the same --nav-overlap geometry used by .root, preferably
via a shared CSS variable or calc() expression across the matching media
breakpoints, so the banner stays in sync if the hero overlap changes.
- Around line 72-86: The .legacyEscrowBanner margin-top values are duplicating
the hero’s breakpoint-specific overlap spacing, so update this rule to reuse the
same shared custom property or overlapping spacing source used by the hero
section. Keep the existing breakpoint behavior in sync by referencing the shared
hero offset in hero-section.module.css rather than hardcoding separate values
here.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8dc91422-bd2e-461c-81bb-9d23276cb680

📥 Commits

Reviewing files that changed from the base of the PR and between d61ab2c and 1d4f3ad.

📒 Files selected for processing (5)
  • src/components/escrow/escrow-page.tsx
  • src/components/homepage/hero-section.module.css
  • src/components/homepage/hero-section.tsx
  • src/components/homepage/legacy-escrow-banner.module.css
  • src/components/homepage/legacy-escrow-banner.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/components/homepage/legacy-escrow-banner.module.css
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/homepage/hero-section.tsx
  • src/components/escrow/escrow-page.tsx
  • src/components/homepage/legacy-escrow-banner.tsx

@bogdanfazakas bogdanfazakas merged commit 2a3fdb2 into main Jul 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Escrow contract selector (legacy withdraw)

5 participants